/*INCLUDES *******************************************************************/
#include "ses_alarm_clock.h"
#include "ses_led.h"

/* DEFINES & MACROS **********************************************************/
#define HOUR_IN_MS 				3600000
#define MIN_IN_MS 				60000
#define RED_FLASH_INTERVALL		300
#define GREEN_FLASH_INTERVALL 	5000
#define HOURS_A_DAY_IN_MS 		(3600000*24)
#define MINUTES_A_HOUR_IN_MS	(60000 * 60)

/* FUNCTION PROTOTYPES *******************************************************/
void startRedFlash();
void stopRedFlash();
uint8_t getHours(uint32_t timeSet);
uint8_t getMinutes(uint32_t timeSet);
void setHours(uint32_t *timeSet, uint8_t hour);
void setMinutes(uint32_t *timeSet, uint8_t hour);

/* FUNCTION DEFINITIONS ******************************************************/

void aClock_init(Fsm *fsm) {
	scheduler_init();
	displayPrint_init();
	button_init();
	led_allInit();
	fsm->alarmSet = 0;
	fsm->timeSet = 0;

	//go to first state
	fsm->state = aClock_setHH;

	//update display
	printf("%d:00  \nRotary: HH++\nJoystick: next:", fsm->timeSet / HOUR_IN_MS);
}

void aClock_setHH(Fsm *fsm, Event *event) {
	switch (event->signal) {
	case ROT_BUTTON:
		//increment hours
		fsm->timeSet += HOUR_IN_MS;

		//calculate overflow and start at 0h if hour value is bigger than 23
		if (getHours(fsm->timeSet) > 23) {
			setHours(&(fsm->timeSet), 0);
		}

		//display time
		displayPrint_setCursor(0, 0);
		printf("%d:00  ", fsm->timeSet / HOUR_IN_MS);

		break;

	case JS_BUTTON:
		//go to next state
		fsm->state = aClock_setMM;

		//update display
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%d:00\nRotary: MM++\nJoystick: next:",
				fsm->timeSet / HOUR_IN_MS);
		break;

	default:
		break;
	}
}

void aClock_setMM(Fsm *fsm, Event *event) {
	type_timeRTC time;

	switch (event->signal) {
	case ROT_BUTTON:
		//increment minutes
		fsm->timeSet += MIN_IN_MS;

		//calculate overflow and start at 0m if minute value is bigger than 59
		if (getMinutes(fsm->timeSet) > 59) {
			setMinutes(&(fsm->timeSet), 0);
		}

		//display time
		displayPrint_setCursor(0, 0);

		printf("%u:%u  ", fsm->timeSet / HOUR_IN_MS,
				(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);

		break;

	case JS_BUTTON:
		//set current time
		scheduler_setTime(fsm->timeSet);
		fsm->timeSet = 0;

		//go to next state normal mode
		fsm->state = aClock_normalMode;
		displayPrint_init();

		//update display

		scheduler_getTimeRTC(&time);
		displayPrint_setCursor(0, 0);
		printf("%u:%u:%u    \nJoystick AL Set\nRotary AL On", time.hour,
				time.minute, time.second);

		break;

	default:
		break;
	}
}

void aClock_normalMode(Fsm *fsm, Event *event) {
	type_timeRTC time;

	switch (event->signal) {
	case SECOND_TIMER:
		//update the display and toggle green led
		scheduler_getTimeRTC(&time);
		displayPrint_setCursor(0, 0);
		printf("%d:%d:%d    ", time.hour, time.minute, time.second);
		led_greenToggle();
		break;

	case JS_BUTTON:
		//go to next state and set alarm hours
		fsm->state = aClock_setAlarmHH;
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%u:%u   Alarm\nRotary: HH++\nJoystick: next:",
				(fsm->timeSet / HOUR_IN_MS),
				(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
		break;

	case ROT_BUTTON:
		//turn on alarm go to next state
		fsm->state = aClock_alarmMode;
		displayPrint_init();
		led_yellowOn();
		fsm->alarmSet = 1;

		//update display
		scheduler_getTimeRTC(&time);
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%u:%u:%u    \nRotary AL Off", time.hour, time.minute,
				time.second);
		break;

	default:
		break;
	}
}

void aClock_alarmMode(Fsm *fsm, Event *event) {
	type_timeRTC time;

	switch (event->signal) {
	case ALARM_TIMER:
		//its alarm time, start buzzing and flash red led
		led_yellowOff();
		fsm->state = aClock_buzzing;
		displayPrint_init();
		scheduler_add(startRedFlash, 0);
		//buzzer_start();

		//update display
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%u:%u:%u   \nAUFWACHEN!!!!\nAny Key To Stop", time.hour,
				time.minute, time.second);
		break;

	case SECOND_TIMER:
		//update display and toggle green led
		scheduler_getTimeRTC(&time);
		displayPrint_setCursor(0, 0);
		printf("%u:%u:%u   \nJoystick AL Set\nRotary AL On", time.hour,
				time.minute, time.second);
		led_greenToggle();
		break;

	case ROT_BUTTON:
		//go back to normal mode
		fsm->state = aClock_normalMode;
		displayPrint_init();

		//turn off alarm
		led_yellowOff();
		fsm->alarmSet = 0;

		//update display
		scheduler_getTimeRTC(&time);
		displayPrint_setCursor(0, 0);
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time.hour,
				time.minute, time.second);
		break;

	default:
		break;
	}

}
void aClock_setAlarmHH(Fsm *fsm, Event *event) {
	switch (event->signal) {
	case ROT_BUTTON:
		//increment hours
		fsm->timeSet += HOUR_IN_MS;

		//calculate overflow and start at 0h if hour value is bigger than 23
		if (getHours(fsm->timeSet) > 23) {
			setHours(&(fsm->timeSet), 0);
		}

		//display time
		displayPrint_setCursor(0, 0);
		printf("%d:00  ", fsm->timeSet / HOUR_IN_MS);

		break;

	case JS_BUTTON:
		//go to next state for setting alarm minutes
		fsm->state = aClock_setAlarmMM;

		//update display
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%d:%d    Alarm\nRotary: MM++\nJoystick: next:",
				fsm->timeSet / HOUR_IN_MS,
				(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
		break;

	default:
		break;
	}
}

void aClock_setAlarmMM(Fsm *fsm, Event *event) {
	switch (event->signal) {
	case ROT_BUTTON:
		//incement minutes
		fsm->timeSet += MIN_IN_MS;

		//calculate overflow and start at 0m if minute value is bigger than 59
		if (getMinutes(fsm->timeSet) > 59) {
			setMinutes(&(fsm->timeSet), 0);
		}

		//display time
		displayPrint_setCursor(0, 0);
		printf("%d:%d   ", fsm->timeSet / HOUR_IN_MS,
				(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);

		break;

	case JS_BUTTON:
		//go to alarm mode
		fsm->state = aClock_alarmMode;
		fsm->alarmSet = 1;
		led_yellowOn();

		//update display
		displayPrint_init();
		type_timeRTC time;
		scheduler_getTimeRTC(&time);
		displayPrint_setCursor(0, 0);
		printf("%d:%d:%d   \nRotary AL Off", time.hour, time.minute, time.second);
		break;

	default:
		break;
	}
}
void aClock_buzzing(Fsm *fsm, Event *event) {
	type_timeRTC time;

	switch (event->signal) {
	case ROT_BUTTON:
		//stop the buzzing and return to normal mode
		stopRedFlash();
		led_yellowOff();
		buzzer_stop();
		fsm->state = aClock_normalMode;

		//update display
		scheduler_getTimeRTC(&time);
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%d:%d:%d   \nJoystick AL Set\nRotary AL On", time.hour,
				time.minute, time.second);
		break;

	case JS_BUTTON:
		//stop the buzzing and return to normal mode
		stopRedFlash();
		led_yellowOff();
		buzzer_stop();
		fsm->state = aClock_normalMode;

		//update display
		scheduler_getTimeRTC(&time);
		displayPrint_init();
		displayPrint_setCursor(0, 0);
		printf("%d:%d:%d   \nJoystick AL Set \nRotary AL On", time.hour,
				time.minute, time.second);
		break;

	default:
		break;

	}
}

/**
 * Starts flashing of the red led.
 * Stop with "stopRedFlash()".
 */
void startRedFlash() {
	led_redToggle();
	scheduler_add(startRedFlash, RED_FLASH_INTERVALL);
}

/**
 * Stops flashing of the red led.
 * Start with "startRedFlash()".
 */
void stopRedFlash() {
	scheduler_remove(startRedFlash);
}

/**
 * Calculates the clock hours of a ms value.
 *
 * @return the hour
 */
uint8_t getHours(uint32_t timeSet) {
	return timeSet / HOUR_IN_MS;
}

/**
 * Sets the clock hour of a ms value.
 *
 * @param timeSet the ms value
 * @param hour the hour which should be set
 */
void setHours(uint32_t *timeSet, uint8_t hour) {
	uint32_t remainder = 0;
	remainder = *timeSet % HOUR_IN_MS;
	*timeSet = (hour * HOUR_IN_MS) + remainder;
}

/**
 * Calculates the clock minutes of a ms value.
 *
 * @return the minute
 */
uint8_t getMinutes(uint32_t timeSet) {
	return (timeSet % HOUR_IN_MS) / MINUTES_A_HOUR_IN_MS;
}

/**
 * Sets the clock minutes of a ms value.
 *
 * @param timeSet the ms value
 * @param minutes the minute which should be set
 */
void setMinutes(uint32_t *timeSet, uint8_t minutes) {
	uint32_t hours = getHours(*timeSet);
	*timeSet = (hours * HOUR_IN_MS) + (minutes * MIN_IN_MS);
}
